package com.yyw.foodie.controller;

import com.yyw.foodie.bo.users.UserLoginBo;
import com.yyw.foodie.common.Result;
import com.yyw.foodie.constant.BaseConstant;
import com.yyw.foodie.pojo.UsersEntity;
import com.yyw.foodie.service.UsersService;
import com.yyw.foodie.utils.CookieUtils;
import com.yyw.foodie.utils.JsonUtils;
import com.yyw.foodie.utils.RedisOperator;
import com.yyw.foodie.vo.UsersVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Objects;
import java.util.UUID;

/**
 * @author 易煜玮
 * @ClassName LoginController.java
 * @Description TODO
 * @createTime 2021年06月26日 17:21:00
 */
@Controller
public class LoginController {

    @Autowired
    private UsersService usersService;

    @Autowired
    private RedisOperator redisOperator;

    public static final String GLOBAL_TOKEN_CAS = "casGlobalToken";
    public static final String TEMP_TOKEN_CAS = "casTempToken";
    public static final String COOKIE_TOKEN_CAS = "cookieTempToken";

    /**
     * 判断用户是否登录
     *
     * @param returnUrl
     * @param model
     * @return
     */
    @GetMapping("/login")
    public String login(String returnUrl, Model model, HttpServletRequest request) {
        // 从请求中获取是否有全局唯一门票
        String globalToken = CookieUtils.getCookieValue(request, COOKIE_TOKEN_CAS);
        String userId = redisOperator.get(GLOBAL_TOKEN_CAS + ":" + globalToken);
        if (!StringUtils.isBlank(userId)) {
            // 生成临时门票，返回用户请求的页面
            String tempToken = UUID.randomUUID().toString().trim();
            redisOperator.set(TEMP_TOKEN_CAS + ":" + tempToken, tempToken, 120);
            return "redirect:" + returnUrl + "?tmpTicket=" + tempToken;
        }

        model.addAttribute("returnUrl", returnUrl);
        return "login";
    }


    @PostMapping("/doLogin")
    public String doLogin(String userName, String passWord, String returnUrl, HttpServletRequest request, HttpServletResponse response, Model model) {
        model.addAttribute("returnUrl", returnUrl);
        model.addAttribute("userName", userName);
        model.addAttribute("passWord", passWord);


        // 1. 校验登录
        if (!StringUtils.isNoneBlank(userName, passWord)) {
            model.addAttribute("errorMsg", "请填写用户名或密码");
            return "login";
        }
        try {
            UserLoginBo userLogin = new UserLoginBo();
            userLogin.setUserName(userName);
            userLogin.setPassWord(passWord);
            UsersEntity entity = usersService.login(userLogin);
            if (null == entity) {
                model.addAttribute("errorMsg", "用户名或密码输入错误");
                return "login";
            }
            UsersVo vo = new UsersVo();
            BeanUtils.copyProperties(entity, vo);
            vo.setUsername(entity.getUserName());
            vo.setNickname(entity.getNickName());

            // 2. 保存用户会话信息至redis
            String uuid = UUID.randomUUID().toString().trim();
            vo.setUserUniqueToken(uuid);
            // 保存用户会话到redis
            redisOperator.set(BaseConstant.USER_TOKEN_REDIS_KEY + vo.getId(), JsonUtils.objectToJson(vo));


            // 3. 创建用户全局门票，用于表示在CAS端登录成功了
            String globalToken = UUID.randomUUID().toString().trim();
            // 关联用户ID，保存到redis
            redisOperator.set(GLOBAL_TOKEN_CAS + ":" + globalToken, vo.getId());
            setCookie(COOKIE_TOKEN_CAS, globalToken, response, null);

            // 4. 创建临时票据，用于回跳跳转
            String tempToken = UUID.randomUUID().toString().trim();
            redisOperator.set(TEMP_TOKEN_CAS + ":" + tempToken, tempToken, 120);

            return "redirect:" + returnUrl + "?tmpTicket=" + tempToken;
        } catch (Exception e) {
            e.printStackTrace();
            return "login";
        }
    }


    /**
     * 校验临时门票的真实性
     * @param tmpTicket   临时门票
     * @return
     */
    @PostMapping("/verifyTmpTicket")
    @ResponseBody
    public Result verifyTmpTicket(String tmpTicket, HttpServletRequest request, HttpServletResponse response) {
        // 1. 校验临时票据的正确性
        if (StringUtils.isBlank(tmpTicket)) {
            return Result.errorMsg("临时票据不能为空");
        }
        String redisTempToken = redisOperator.get(TEMP_TOKEN_CAS + ":" + tmpTicket);
        if (StringUtils.isBlank(redisTempToken) || !Objects.equals(tmpTicket, redisTempToken)) {
            return Result.errorMsg("临时票据已过期，请重新登录");
        }

        // 2. 删除临时票据
        redisOperator.del(TEMP_TOKEN_CAS + ":" + tmpTicket);

        // 3. 获取用户全局门票
        String globalToken = CookieUtils.getCookieValue(request, COOKIE_TOKEN_CAS);
        if (StringUtils.isBlank(globalToken)) {
            return Result.errorMsg("用户全局票据不存在，请重新登录");
        }

        // 4. 通过用户全局门票获取用户ID
        String userId = redisOperator.get(GLOBAL_TOKEN_CAS + ":" + globalToken);
        if (StringUtils.isBlank(userId)) {
            return Result.errorMsg("用户全局票据不存在，请重新登录");
        }

        // 3. 获取用户会话
        String userInfo = redisOperator.get(BaseConstant.USER_TOKEN_REDIS_KEY + userId);
        return Result.ok(JsonUtils.jsonToPojo(userInfo, UsersVo.class));
    }


    @PostMapping("/logout")
    @ResponseBody
    public Result logout(String userId, HttpServletRequest request, HttpServletResponse response) {
        if (StringUtils.isBlank(userId)) {
            return Result.errorMsg("参数异常");
        }
        // 删除全局门票
        String globalToken = CookieUtils.getCookieValue(request, COOKIE_TOKEN_CAS);
        redisOperator.del(GLOBAL_TOKEN_CAS + ":" + globalToken);
        // 删除用户会话
        redisOperator.del(BaseConstant.USER_TOKEN_REDIS_KEY + userId);
        // 删除cookie信息
        setCookie(COOKIE_TOKEN_CAS, null, response, -1);
        return Result.ok();
    }


    private void setCookie(String key, String val, HttpServletResponse response, Integer ttl) {
        Cookie cookie = new Cookie(key, val);
        cookie.setDomain("sso.com");
        cookie.setPath("/");
        if (ttl != null) {
            cookie.setMaxAge(ttl);
        }
        response.addCookie(cookie);
    }
}
